package com.ruoyi.business.modbus.serial.jserialcomm;

import com.fazecast.jSerialComm.SerialPort;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;

/**
 * 基于jSerialComm的串口输入流实现
 * 
 * @author ruoyi
 * @date 2024-01-01
 */
public class JSerialCommInputStream extends InputStream {
    
    private static final Logger log = LoggerFactory.getLogger(JSerialCommInputStream.class);
    
    private final SerialPort serialPort;
    private boolean closed = false;
    
    /**
     * 构造函数
     * 
     * @param serialPort jSerialComm串口对象
     */
    public JSerialCommInputStream(SerialPort serialPort) {
        this.serialPort = serialPort;
        log.debug("创建jSerialComm串口输入流");
    }
    
    @Override
    public int read() throws IOException {
        checkClosed();
        
        byte[] buffer = new byte[1];
        int bytesRead = serialPort.readBytes(buffer, 1);
        
        if (bytesRead > 0) {
            return buffer[0] & 0xFF; // 转换为无符号整数
        }
        
        return -1; // 无数据或超时
    }
    
    @Override
    public int read(byte[] b) throws IOException {
        return read(b, 0, b.length);
    }
    
    @Override
    public int read(byte[] b, int off, int len) throws IOException {
        checkClosed();
        
        if (b == null) {
            throw new NullPointerException("缓冲区不能为null");
        }
        if (off < 0 || len < 0 || len > b.length - off) {
            throw new IndexOutOfBoundsException("偏移量或长度参数无效");
        }
        if (len == 0) {
            return 0;
        }
        
        // 如果需要偏移，创建临时缓冲区
        byte[] tempBuffer;
        if (off == 0) {
            tempBuffer = b;
        } else {
            tempBuffer = new byte[len];
        }
        
        int bytesRead = serialPort.readBytes(tempBuffer, len);
        
        if (bytesRead > 0) {
            if (off > 0) {
                // 复制到目标缓冲区
                System.arraycopy(tempBuffer, 0, b, off, bytesRead);
            }
            log.trace("读取 {} 字节数据", bytesRead);
            return bytesRead;
        }
        
        return -1; // 无数据或超时
    }
    
    @Override
    public int available() throws IOException {
        checkClosed();
        
        return serialPort.bytesAvailable();
    }
    
    @Override
    public void close() throws IOException {
        if (!closed) {
            closed = true;
            log.debug("jSerialComm串口输入流已关闭");
        }
    }
    
    /**
     * 检查流是否已关闭
     */
    private void checkClosed() throws IOException {
        if (closed) {
            throw new IOException("输入流已关闭");
        }
        if (serialPort == null || !serialPort.isOpen()) {
            throw new IOException("串口未打开");
        }
    }
    
    /**
     * 设置读取超时时间
     * 
     * @param timeoutMs 超时时间(毫秒)
     */
    public void setReadTimeout(int timeoutMs) {
        if (serialPort != null && serialPort.isOpen()) {
            serialPort.setComPortTimeouts(
                SerialPort.TIMEOUT_READ_SEMI_BLOCKING | SerialPort.TIMEOUT_WRITE_BLOCKING, 
                timeoutMs, 0);
            log.debug("设置读取超时时间: {}ms", timeoutMs);
        }
    }
    
    /**
     * 等待数据可用
     * 
     * @param timeoutMs 超时时间(毫秒)
     * @return 是否有数据可用
     */
    public boolean waitForData(int timeoutMs) throws IOException {
        checkClosed();
        
        long startTime = System.currentTimeMillis();
        
        while (System.currentTimeMillis() - startTime < timeoutMs) {
            if (serialPort.bytesAvailable() > 0) {
                return true;
            }
            
            // 短暂等待
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return false;
            }
        }
        
        return serialPort.bytesAvailable() > 0;
    }
    
    /**
     * 清空输入缓冲区
     */
    public void flushInput() throws IOException {
        checkClosed();
        
        // 读取并丢弃所有可用数据
        int available = serialPort.bytesAvailable();
        if (available > 0) {
            byte[] discardBuffer = new byte[available];
            serialPort.readBytes(discardBuffer, available);
            log.debug("已清空输入缓冲区: {} 字节", available);
        }
    }
    
    /**
     * 读取指定数量的字节，阻塞直到读取完成或超时
     * 
     * @param buffer 缓冲区
     * @param length 要读取的字节数
     * @param timeoutMs 超时时间(毫秒)
     * @return 实际读取的字节数
     */
    public int readBytes(byte[] buffer, int length, int timeoutMs) throws IOException {
        checkClosed();
        
        if (buffer == null || buffer.length < length) {
            throw new IllegalArgumentException("缓冲区无效");
        }
        
        // 临时设置超时时间
        int originalTimeout = serialPort.getReadTimeout();
        serialPort.setComPortTimeouts(
            SerialPort.TIMEOUT_READ_BLOCKING, 
            timeoutMs, 0);
        
        try {
            int bytesRead = serialPort.readBytes(buffer, length);
            log.trace("阻塞读取 {} 字节数据，实际读取: {}", length, bytesRead);
            return bytesRead;
        } finally {
            // 恢复原始超时设置
            serialPort.setComPortTimeouts(
                SerialPort.TIMEOUT_READ_SEMI_BLOCKING | SerialPort.TIMEOUT_WRITE_BLOCKING, 
                originalTimeout, 0);
        }
    }
}
